const char *help = "\
progname: .cc\n\
code2html: Ce programme améliore le contraste d'une image.\n\
version: Torch3 vision2.0, 2003-2005\n\
(c) Ewelle Richard (eerichard@ifi.edu.vn)\n";
 
 #include "ImageGray.h"
 #include "ImageRgb.h"
 #include "tiffDiskXFile.h"
 #include "ipHisto.h"
 #include "CmdLine.h"
 #include "Line2D.h"	
 #include "Rectangle2D.h"
 #include "xtprobeImageDiskXFile.h"
 
 using namespace Torch;
 
	
	const int hauteur=230; 
	const int Maxunite=220; 

	struct Point
	{
	int abs;
        int ord;
        Point(int x=30, int y=20)
        {
     	 abs = x;
   	 ord = y;
         return ;
	} ;
        Point &operator=(const Point &P)
	{
    	 abs = P.abs;
   	 ord = P.ord;
   	 return *this;
	};

	};

//************** Extraction des coordonnées d'un point à partir d'une chaine de caractère **************//

Point extractCoord(char *string)
{
	Point coord;
	char *val1 = new char[100];
	char *val2 = new char[100];
	int i, j;
	
// Extraction de la première valeure

	for (i = 0; string[i] != ','; i ++)
		val1[i] = string[i];
	val1[i ++] = (char)NULL;	
	coord.abs = atoi(val1);
	
// Extraction de la deuxième valeure

	for (j = 0; string[j + i] != (char)NULL; j ++)
		val2[j] = string[j + i];
	val2[j] = (char)NULL;
	coord.ord = atoi(val2);
	return coord;
}

//******   Dessin de l'histogramme *******//

void drawingHistogramme(real *h,Image *origine,Image *sortie)
{
// Colorier le fond de l'image en blanc.


	for(int x=0;x< 256;x++)
        {
         for(int y=0;y< hauteur;y++)   
     
          sortie->drawpixel(x,y,white);             
	
	} 
// Tracé de la histogramme

	real max = 0;
	int output_size = 256;

	for (int i = 0; i < output_size; i ++)
		if (max < h[i]) max = h[i];
	
	int unite = (int)max / Maxunite;
	
	//Chaque valeur de couleur est équivalente à trois ligne verticale

	for(int i = 0; i < 256; i++)
	{
		h[i] /= unite;
		for (int j = 0; j < 1; j ++)
                 {
		  int abs=hauteur-(int)h[i];	
		  sortie->drawline(i + j, hauteur, i + j, abs, black);
                       
	         }
        }
}


//******   Construction de l'histograme *******//

void settingHistogramme(Image *origine,Image *sortie)
{
       
	ipCore *histo = NULL;
 
 	histo = new ipHisto(origine->width, origine->height, "gray");
 	histo->setBOption("normalise", false);
 	histo->process(origine);
 
 	real *hist = histo->seq_out->frames[0];

 	drawingHistogramme(hist,origine,sortie);
         
	
}

//******   Sauvegarde des images au format TIFF *******//

void savingImage(Image *sortie,char *nom)
{	
	char *tiff_filename = new char[80];    
	tiffDiskXFile *tiff_file = NULL;

	strcpy(tiff_filename, nom);
	strcat(tiff_filename, ".tiff");

 	tiff_file = new tiffDiskXFile(tiff_filename, "w");
 	tiff_file->writeHeader(sortie->width, sortie->height);
 	tiff_file->writePixmap(sortie->pixmap);
 	delete tiff_file;

}
//******   Construction du nom de fichier *******//

char * setTitle(char* string,char* prefix)
{
	
	char *str = new char[300];
	char *str1 = new char[300];
	int i, j=0;
	
// Extraction du nom du fichier sans extension

	for (i = 0; string[i] != '\0'; i ++)
	 {
           if(string[i]!='/')
            {
  	     str[j] = string[i];
             j++;
            }
	   else
            {
	    j=0;
             }             
         }
	str[j ++] = (char)NULL;

	for (i = 0; str[i] != '.'; i ++)
	 {
          str1[i] = str[i];
         }
            
        str1[i] = (char)NULL;	 
 
	strcat(str1, prefix);
	return str1;

}

//******   Construction de l'histograme *******//

void gettingHistogramme(char* name,Image *origine)
{
	Image *sortie = NULL;	
	sortie = new ImageRgb(256, hauteur);
        settingHistogramme(origine,sortie); 	
        savingImage(sortie,name);

	delete sortie;
         	
}


//******   Vérification des coordonnées de points *******//

void checkValues(Point * Points)
{
	int i;

 // verifier l'intervale de chaque coordonnée entre 0 et 255
	
	for (i = 0; i < 4; i ++)
         
	  if ((Points[i].abs < 0 || Points[i].abs > 255)||(Points[i].ord < 0 ||Points[i].ord > 255))
	      {
			error("Coordonnées incorrect Point %d!!!", i+1);
			exit(1);
	       }

//  Verifier l'ordre de précédence des points entrés

	for (i = 0; i < 3; i ++)
		if (Points[i].abs > Points[i+1].abs)
		{
			error("Verifier l'ordre des points  %d>%d at %d!!!", Points[i].abs, Points[i+1].abs, i);
			exit(1);
		}

}


//******   Initialisation de la table LUT *******//

int* lookUpTable(Point * Points)
{
	int i, j;
	int abs, ord;
	int* TabLut = new int[256];
	
	//Le segment allant de (0,0) au point 0: points[0]

	for (j = 0; j < Points[0].abs; j ++)
		TabLut[j] = j * Points[0].ord / Points[0].abs;
		
	//Les segments allant d'un point i à un point i+1 avec (i=0,1,2)

	for (i = 0; i < 3; i ++)
	{
		abs = Points[i+1].abs - Points[i].abs;
		ord = Points[i+1].ord - Points[i].ord;
		for (; j < Points[i + 1].abs; j ++)
			TabLut[j] = Points[i].ord + (j - Points[i].abs) * ord / abs;
	}
	
	//Le dernier segment allant du point 3 à (255,255)

	abs = 255 - Points[3].abs;
	ord = 255 - Points[3].ord;
	for (;j < 255; j ++)
		TabLut[j] = Points[3].ord + (j - Points[3].abs) * ord / abs;

	return TabLut;
}


//******** appliqué la LUT pour la modification de contraste de l'image


void modifImage(Image *origine, int *LT)
{
	for (int i = 0; i < origine->width; i ++)	
		for (int j = 0; j < origine->height; j ++)
		{
			origine->get(j,i)[0]=LT[origine->get(j,i)[0]];		
			origine->get(j,i)[1]=LT[origine->get(j,i)[1]];	
			origine->get(j,i)[2]=LT[origine->get(j,i)[2]];	
		}
	origine->updateDataFromPixmap();
}


//******** Déssiner et enregistrer le graphic des points dans le plan x,y


void drawGraphic(char *name, Point *Points)
{

	Image *sortie = NULL;	
	sortie = new ImageRgb(256, 256);

// Colorier le fond de l'image en blanc.

	for(int x=0;x< 256;x++)
        {
         for(int y=0;y< 256;y++)   
     
          sortie->drawpixel(x,y,white);             
	
	} 


	sortie->drawline(0, 255, Points[0].abs, 255 - Points[0].ord, red);

	for (int i = 0; i < 3; i ++)
	{
	  sortie->drawline(Points[i].abs, 255 - Points[i].ord, Points[i+1].abs, 255 - Points[i+1].ord, red);	
	}
	  sortie->drawline(Points[3].abs, 255 - Points[3].ord, 255, 0, red);
	
//Tracé du cadre rectangulaire qui délimite l'image. 

	Point2D A(0,0);
	Point2D B(255,0);
	Point2D C(255,255);
	Point2D D(0,255);

	Rectangle2D r1(A, B, C, D);
	r1.draw(sortie, black);

        savingImage(sortie,name);
}


////////////************* PROGRAMME PRINCIPALE ******************///////////////////////

int main(int argc, char **argv)
{

// Déclarations des variables 

 	bool verbose;
 	bool graphic;
	char* image_filename;
 	char *pa,*pb,*pc,*pd;
        xtprobeImageDiskXFile *image_file = NULL;
 	Image *source = NULL;
	Point *Points;

// Construction de la ligne de commande

  	CmdLine cmd;
	cmd.setBOption("write log", false);
	
// Construction de l'option d'aide

  	cmd.info(help);
   	cmd.addText("\nArguments:");
   	cmd.addSCmdArg("image filename", &image_filename, "image filename");
  	cmd.addText("\nOptions:");

// L'options pour afficher les informations détaillés

  	cmd.addBCmdOption("-verbose", &verbose, false, "verbose");
	cmd.addBCmdOption("-graphic", &graphic, false, "Créer également le graphique des points dans le plan x,y");

// L'options pour choisir les point

  	cmd.addSCmdOption("-a", &pa, "0,0", "Premier point" );
  	cmd.addSCmdOption("-b", &pb, "255,255", "Deuxième point" );
  	cmd.addSCmdOption("-c", &pc, "255,255", "Troisième point" );
  	cmd.addSCmdOption("-d", &pd, "255,255", "Quatrième point" );


 	cmd.read(argc, argv);
 	Points = new Point[4];

// Extraction des coordonnées des points entrées en options sur la ligne de commande et vérification

	Points[0] = extractCoord(pa);
	Points[1] = extractCoord(pb);
	Points[2] = extractCoord(pc);
	Points[3] = extractCoord(pd);
	checkValues(Points);

// Création et chargement de l'image

	source = new ImageRgb();
	source->setBOption("verbose", verbose);

    // Choix: xtprobeImageDiskXFile car il permet de travailler avec plusieurs types d'images 

	image_file = new xtprobeImageDiskXFile(image_filename, "r");
	source->loadImageXFile(image_file);
	delete image_file;

// Afficher les caractéristique de l'image source comme la hauteur et la largeur ainsi que le type de l'image 

 	if(verbose)
 	{
 		print("Information sur l'image source:\n");
 		print("   Largeur = %d\n", source->width);
 		print("   Hauteur = %d\n", source->height);
 		print("   Format = %s (%d)\n", source->coding, source->n_planes);
 	}


//Tracer de l'histogramme de l'image source 

	char *histo1 = new char[300];   
	strcpy(histo1, setTitle(image_filename,(char*)"_Histo"));
        gettingHistogramme(histo1,source);
	
//Construire la table LUT et modifier le contraste l'image source 

	int *TabLut = lookUpTable(Points);
	modifImage(source, TabLut);
	
// Enregistrement de l'image modifiéé

	char *newname = new char[300];   
	strcpy(newname, setTitle(image_filename,(char*)"_Modif"));
        savingImage(source,newname);

//Tracer de l'histogramme de l'image modifiée 

	char *histo2 = new char[300];   
	strcpy(histo2, setTitle(image_filename,(char*)"_Modif_Histo"));
        gettingHistogramme(histo2,source);

// Création du graphic des points dans le plan

 	if(graphic)
 	{

	char *graphname = new char[300];   
	strcpy(graphname, setTitle(image_filename,(char*)"_Graph"));
        drawGraphic(graphname,Points);

	}

	delete source;

	free(Points);     
            
	return(0);
}
